package org.wangchaocheng.cloudgallery.common.utils;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.wangchaocheng.cloudgallery.constant.common.CommonConstants;

/**
 * MP的Wrapper构造器
 *
 * @author wangchaocheng
 * @date 2025/03/28
 */
@Slf4j
public class IBatisPlusWrapperBuilder {

    /**
     * 根据指定DTO字段构造对应DO的QueryWrapper
     * DDD中分层导致domain层的gateway无法访问DO。
     * 为了实现通用的灵活的根据指定字段查询条件
     * 通过使用mp的工具类，实现上层通过lambda传入DTO的指定字段，构造出DO的QueryWrapper
     *
     * @param column      字段
     * @param dataObject  对象
     * @param columnValue 字段值
     * @param <DTO>       DTO
     * @param <DO>        DO
     * @param <T>         T
     * @return {@link QueryWrapper}<{@link DO}>
     */
    public static <DTO, DO, T> QueryWrapper<DO> buildSignCloumnQueryWrapper(SFunction<DTO, ?> column, DO dataObject, T columnValue) {
        // 获取函数接口的lambda元信息
        LambdaMeta meta = LambdaUtils.extract(column);
        // 获取Column的属性名，全小写
        String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
        // 获取Column的对应的数据库表的字段名
        String columnName = getColumnByProperty(dataObject.getClass(), fieldName);
        if (StringUtils.isBlank(columnName)) {
            return null;
        }
        return new QueryWrapper<DO>().eq(columnName, columnValue);
    }

    /**
     * 动态添加排序字段，仅支持单字段排序
     *
     * @param queryWrapper 查询包装器
     * @param clazz        需要操作的数据表对应DO的class
     * @param orderBy      排序字段
     * @param isOrderByAsc 是否升序排序
     * @param <T>          泛型，需要操作的数据表对应DO的类型
     */
    public static <T> void appendDynamicOrderColumn(AbstractWrapper<T, ?, ?> queryWrapper, Class<T> clazz, String orderBy, boolean isOrderByAsc) {
       appendDynamicOrderColumns(queryWrapper, clazz, Set.of(orderBy), isOrderByAsc);
    }

    /**
     * 动态添加排序字段，仅支持单字段排序
     *
     * @param queryWrapper   查询包装器
     * @param clazz          需要操作的数据表对应DO的class
     * @param orderByColumns 排序字段集合
     * @param isOrderByAsc   是否升序排序
     * @param <T>            泛型，需要操作的数据表对应DO的类型
     */
    public static <T> void appendDynamicOrderColumns(AbstractWrapper<T, ?, ?> queryWrapper, Class<T> clazz, Set<String> orderByColumns, boolean isOrderByAsc) {
        if (CollectionUtils.isEmpty(orderByColumns)) {
            return;
        }

        // 获取排序字段的sql片段
        String sqlSegment = orderByColumns.stream()
            .map(item -> getColumnByProperty(clazz, item))
            .filter(Objects::nonNull)
            .collect(Collectors.joining(CommonConstants.COMMA));
        // 获取排序方向
        SqlKeyword orderDirection = Optional.of(isOrderByAsc).orElse(true) ? SqlKeyword.ASC : SqlKeyword.DESC;
        // 往queryWrapper中添加表达式，参数 [排序关键字 排序字段 排序方向]
        queryWrapper.getExpression().add(SqlKeyword.ORDER_BY, () -> sqlSegment, orderDirection);
    }

    private static String getColumnByProperty(Class<?> clazz, String fieldName) {
        // 获取指定dataObject的属性和字段映射关系
        Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(clazz);
        // 获取Column的对应的数据库表的字段名
        ColumnCache columnCache = columnMap.getOrDefault(LambdaUtils.formatKey(fieldName), null);
        if (Objects.isNull(columnCache)) {
            return null;
        }
        return columnCache.getColumn();
    }

}
